using System;
using CommonDomain;
using System.Collections.Generic;

namespace Sponsorship.Domain.Persons
{
    /// <summary>
    /// Represents a person in the Sponsorship domain. A person can be associated with a sponsored child.
    /// </summary>
    public class Person : RelaxedAggregateBase
    {
        private readonly HashSet<Guid> _personAssociations = new HashSet<Guid>();
        private readonly HashSet<Guid> _suggestedPresents = new HashSet<Guid>();

        public Person(Guid id, string firstName, string lastName, DateTime? dateOfBirth, string telephoneNumber, SponsoringStatus status, string comment)
            : this(id, null)
        {
            if (id == Guid.Empty)
                throw new ArgumentException("Id must not be empty.", "id");

            RaiseEvent(new PersonEntered(id, firstName, lastName, dateOfBirth, telephoneNumber, status, comment));
        }

        // Infrastructure constructor used by AggregateFactory
        private Person(Guid id, IMemento snapshot)
        {
            if (snapshot != null)
                throw new NotSupportedException("This class does not support snapshots.");
            Id = id;
        }

        public void Change(string firstName, string lastName, DateTime? dateOfBirth, string telephoneNumber, SponsoringStatus status, string comment)
        {
            RaiseEvent(new PersonChanged(Id, firstName, lastName, dateOfBirth, telephoneNumber, status, comment));
        }

        public void Delete()
        {
            RaiseEvent(new PersonDeleted());
        }

        public void AssociatePerson(Guid personAssociationId, Guid associatedPersonId, Guid personAssociationTemplateId, string associationName, string comment)
        {
            RaiseEvent(new PersonAssociationEntered(Id, personAssociationId, associatedPersonId, personAssociationTemplateId, associationName, comment));
        }

        public void ChangePersonAssociation(Guid personAssociationId, Guid associatedPersonId, Guid personAssociationTemplateId, string associationName, string comment)
        {
            if (!_personAssociations.Contains(personAssociationId))
                throw new InvalidOperationException("Cannot change person association that was never added.");

            RaiseEvent(new PersonAssociationChanged(Id, personAssociationId, associatedPersonId, personAssociationTemplateId, associationName, comment));
        }

        public void DeletePersonAssociation(Guid personAssociationId)
        {
            if (!_personAssociations.Contains(personAssociationId))
                throw new InvalidOperationException("Cannot delete person association that was never entered.");

            RaiseEvent(new PersonAssociationDeleted(Id, personAssociationId));
        }
        
        public void SuggestPresent(Guid suggestedPresentId, Guid presentTemplateId, PresentData presentData, string comment)
        {
            RaiseEvent(new PresentSuggested(Id, suggestedPresentId, presentTemplateId, presentData, comment));
        }

        public void ChangeSuggestedPresent(Guid suggestedPresentId, Guid presentTemplateId, PresentData presentData, string comment)
        {
            if (!_suggestedPresents.Contains(suggestedPresentId))
                throw new InvalidOperationException("Cannot change a suggested present that was never entered.");

            RaiseEvent(new SuggestedPresentChanged(Id, suggestedPresentId, presentTemplateId, presentData, comment));
        }

        public void DeleteSuggestedPresent(Guid suggestedPresentId)
        {
            if (!_suggestedPresents.Contains(suggestedPresentId))
                throw new InvalidOperationException("Cannot delete a suggested present that was never entered.");

            RaiseEvent(new SuggestedPresentDeleted(Id, suggestedPresentId));
        }

        protected override IMemento GetSnapshot()
        {
            throw new NotSupportedException("This class does not support snapshots.");
        }

        private void Apply(PersonAssociationEntered @event)
        {
            this._personAssociations.Add(@event.PersonAssociationId);
        }

        private void Apply(PresentSuggested @event)
        {
            this._suggestedPresents.Add(@event.SuggestedPresentId);
        }
    }
}